2 # Copyright (c) the JPEG XL Project Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style
5 # license that can be found in the LICENSE file.
7 # Continuous integration helper module. This module is meant to be called from
8 # workflows during the continuous integration build, as well as from the
9 # command line for developers.
15 MYDIR
=$
(dirname $
(realpath
"$0"))
17 ### Environment parameters:
18 TEST_STACK_LIMIT
="${TEST_STACK_LIMIT:-256}"
19 BENCHMARK_NUM_THREADS
="${BENCHMARK_NUM_THREADS:-0}"
20 BUILD_CONFIG
=${BUILD_CONFIG:-}
21 CMAKE_BUILD_TYPE
=${CMAKE_BUILD_TYPE:-RelWithDebInfo}
22 CMAKE_PREFIX_PATH
=${CMAKE_PREFIX_PATH:-}
23 CMAKE_C_COMPILER_LAUNCHER
=${CMAKE_C_COMPILER_LAUNCHER:-}
24 CMAKE_CXX_COMPILER_LAUNCHER
=${CMAKE_CXX_COMPILER_LAUNCHER:-}
25 CMAKE_MAKE_PROGRAM
=${CMAKE_MAKE_PROGRAM:-}
26 SKIP_BUILD
="${SKIP_BUILD:-0}"
27 SKIP_TEST
="${SKIP_TEST:-0}"
28 FASTER_MSAN_BUILD
="${FASTER_MSAN_BUILD:-0}"
29 TARGETS
="${TARGETS:-all doc}"
30 TEST_SELECTOR
="${TEST_SELECTOR:-}"
31 BUILD_TARGET
="${BUILD_TARGET:-}"
32 ENABLE_WASM_SIMD
="${ENABLE_WASM_SIMD:-0}"
33 if [[ -n "${BUILD_TARGET}" ]]; then
34 BUILD_DIR
="${BUILD_DIR:-${MYDIR}/build-${BUILD_TARGET%%-*}}"
36 BUILD_DIR
="${BUILD_DIR:-${MYDIR}/build}"
38 # Whether we should post a message in the MR when the build fails.
39 POST_MESSAGE_ON_ERROR
="${POST_MESSAGE_ON_ERROR:-1}"
40 # By default, do a lightweight debian HWY package build.
41 HWY_PKG_OPTIONS
="${HWY_PKG_OPTIONS:---set-envvar=HWY_EXTRA_CONFIG=-DBUILD_TESTING=OFF -DHWY_ENABLE_EXAMPLES=OFF -DHWY_ENABLE_CONTRIB=OFF}"
43 # Set default compilers to clang if not already set
44 export CC
=${CC:-clang}
45 export CXX
=${CXX:-clang++}
47 # Time limit for the "fuzz" command in seconds (0 means no limit).
48 FUZZER_MAX_TIME
="${FUZZER_MAX_TIME:-0}"
53 if [[ "${BUILD_TARGET%%-*}" == "x86_64" ||
54 "${BUILD_TARGET%%-*}" == "i686" ]]; then
55 # Default to building all targets, even if compiler baseline is SSE4
56 HWY_BASELINE_TARGETS
=${HWY_BASELINE_TARGETS:-HWY_EMU128}
58 HWY_BASELINE_TARGETS
=${HWY_BASELINE_TARGETS:-}
61 # Convenience flag to pass both CMAKE_C_FLAGS and CMAKE_CXX_FLAGS
62 CMAKE_FLAGS
=${CMAKE_FLAGS:-}
63 CMAKE_C_FLAGS
="${CMAKE_C_FLAGS:-} ${CMAKE_FLAGS}"
64 CMAKE_CXX_FLAGS
="${CMAKE_CXX_FLAGS:-} ${CMAKE_FLAGS}"
66 CMAKE_CROSSCOMPILING_EMULATOR
=${CMAKE_CROSSCOMPILING_EMULATOR:-}
67 CMAKE_EXE_LINKER_FLAGS
=${CMAKE_EXE_LINKER_FLAGS:-}
68 CMAKE_FIND_ROOT_PATH
=${CMAKE_FIND_ROOT_PATH:-}
69 CMAKE_MODULE_LINKER_FLAGS
=${CMAKE_MODULE_LINKER_FLAGS:-}
70 CMAKE_SHARED_LINKER_FLAGS
=${CMAKE_SHARED_LINKER_FLAGS:-}
71 CMAKE_TOOLCHAIN_FILE
=${CMAKE_TOOLCHAIN_FILE:-}
73 if [[ "${ENABLE_WASM_SIMD}" -ne "0" ]]; then
74 CMAKE_CXX_FLAGS
="${CMAKE_CXX_FLAGS} -msimd128"
75 CMAKE_C_FLAGS
="${CMAKE_C_FLAGS} -msimd128"
76 CMAKE_EXE_LINKER_FLAGS
="${CMAKE_EXE_LINKER_FLAGS} -msimd128"
79 if [[ "${ENABLE_WASM_SIMD}" -eq "2" ]]; then
80 CMAKE_CXX_FLAGS
="${CMAKE_CXX_FLAGS} -DHWY_WANT_WASM2"
81 CMAKE_C_FLAGS
="${CMAKE_C_FLAGS} -DHWY_WANT_WASM2"
84 if [[ -z "${BUILD_CONFIG}" ]]; then
85 TOOLS_DIR
="${BUILD_DIR}/tools"
87 TOOLS_DIR
="${BUILD_DIR}/tools/${BUILD_CONFIG}"
90 if [[ ! -z "${HWY_BASELINE_TARGETS}" ]]; then
91 CMAKE_CXX_FLAGS
="${CMAKE_CXX_FLAGS} -DHWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS}"
94 # Version inferred from the CI variables.
95 CI_COMMIT_SHA
=${GITHUB_SHA:-}
96 JPEGXL_VERSION
=${JPEGXL_VERSION:-}
98 # Benchmark parameters
99 STORE_IMAGES
=${STORE_IMAGES:-1}
100 BENCHMARK_CORPORA
="${MYDIR}/third_party/corpora"
102 # Local flags passed to sanitizers.
109 -fsanitize=float-cast-overflow
110 -fsanitize=float-divide-by-zero
111 -fsanitize=integer-divide-by-zero
113 -fsanitize=object-size
114 -fsanitize=pointer-overflow
116 -fsanitize=returns-nonnull-attribute
117 -fsanitize=shift-base
118 -fsanitize=shift-exponent
119 -fsanitize=unreachable
122 -fno-sanitize-recover=undefined
123 # Brunsli uses unaligned accesses to uint32_t, so alignment is just a warning.
124 -fsanitize-recover=alignment
126 # -fsanitize=function doesn't work on aarch64 and arm.
127 if [[ "${BUILD_TARGET%%-*}" != "aarch64" &&
128 "${BUILD_TARGET%%-*}" != "arm" ]]; then
133 if [[ "${BUILD_TARGET%%-*}" != "arm" ]]; then
135 -fsanitize=signed-integer-overflow
139 CLANG_TIDY_BIN
=$
(which clang-tidy-6.0 clang-tidy-7 clang-tidy-8 clang-tidy
2>/dev
/null |
head -n 1)
140 # Default to "cat" if "colordiff" is not installed or if stdout is not a tty.
142 COLORDIFF_BIN
=$
(which colordiff
cat 2>/dev
/null |
head -n 1)
146 FIND_BIN
=$
(which gfind
find 2>/dev
/null |
head -n 1)
147 # "false" will disable wine64 when not installed. This won't allow
149 WINE_BIN
=$
(which wine64 false
2>/dev
/null |
head -n 1)
151 CLANG_VERSION
="${CLANG_VERSION:-}"
152 # Detect the clang version suffix and store it in CLANG_VERSION. For example,
153 # "6.0" for clang 6 or "7" for clang 7.
154 detect_clang_version
() {
155 if [[ -n "${CLANG_VERSION}" ]]; then
158 local clang_version
=$
("${CC:-clang}" --version |
head -n1)
159 clang_version
=${clang_version#"Debian "}
160 clang_version
=${clang_version#"Ubuntu "}
162 case "${clang_version}" in
167 # Any other clang version uses just the major version number.
168 local suffix
="${clang_version#clang version }"
169 CLANG_VERSION
="${suffix%%.*}"
172 # We can't use asan or msan in the emcc case.
175 echo "Unknown clang version: ${clang_version}" >&2
180 # Temporary files cleanup hooks.
183 if [[ ${#CLEANUP_FILES[@]} -ne 0 ]]; then
184 rm -fr "${CLEANUP_FILES[@]}"
191 # Always cleanup the CLEANUP_FILES.
195 trap 'retcode=$?; { set +x; } 2>/dev/null; on_exit ${retcode}' INT TERM EXIT
198 # These variables are populated when calling merge_request_commits().
200 # The current hash at the top of the current branch or merge request branch (if
201 # running from a merge request pipeline).
203 # The common ancestor between the current commit and the tracked branch, such
204 # as main. This includes a list
207 # Populate MR_HEAD_SHA and MR_ANCESTOR_SHA.
208 merge_request_commits
() {
209 { set +x
; } 2>/dev
/null
210 # GITHUB_SHA is the current reference being build in GitHub Actions.
211 if [[ -n "${GITHUB_SHA:-}" ]]; then
212 # GitHub normally does a checkout of a merge commit on a shallow repository
213 # by default. We want to get a bit more of the history to be able to diff
214 # changes on the Pull Request if needed. This fetches 10 more commits which
215 # should be enough given that PR normally should have 1 commit.
216 git
-C "${MYDIR}" fetch
-q origin
"${GITHUB_SHA}" --depth 10
217 if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
218 MR_HEAD_SHA
="$(git rev-parse "FETCH_HEAD^
2" 2>/dev/null ||
219 echo "${GITHUB_SHA}")"
221 MR_HEAD_SHA
="${GITHUB_SHA}"
224 MR_HEAD_SHA
=$
(git
-C "${MYDIR}" rev-parse
-q "HEAD")
227 if [[ -n "${GITHUB_BASE_REF:-}" ]]; then
228 # Pull request workflow in GitHub Actions. GitHub checkout action uses
229 # "origin" as the remote for the git checkout.
230 git
-C "${MYDIR}" fetch
-q origin
"${GITHUB_BASE_REF}"
231 MR_ANCESTOR_SHA
=$
(git
-C "${MYDIR}" rev-parse
-q FETCH_HEAD
)
233 # We are in a local branch, not a pull request workflow.
234 MR_ANCESTOR_SHA
=$
(git
-C "${MYDIR}" rev-parse
-q HEAD@
{upstream
} || true
)
237 if [[ -z "${MR_ANCESTOR_SHA}" ]]; then
238 echo "Warning, not tracking any branch, using the last commit in HEAD.">&2
239 # This prints the return value with just HEAD.
240 MR_ANCESTOR_SHA
=$
(git
-C "${MYDIR}" rev-parse
-q "${MR_HEAD_SHA}^")
242 # GitHub runs the pipeline on a merge commit, no need to look for the common
243 # ancestor in that case.
244 if [[ -z "${GITHUB_BASE_REF:-}" ]]; then
245 MR_ANCESTOR_SHA
=$
(git
-C "${MYDIR}" merge-base \
246 "${MR_ANCESTOR_SHA}" "${MR_HEAD_SHA}")
253 # Set up and export the environment variables needed by the child processes.
255 if [[ "${BUILD_TARGET}" == *mingw32
]]; then
256 # Wine needs to know the paths to the mingw dlls. These should be
258 WINEPATH
=$
("${CC:-clang}" -print-search-dirs --target="${BUILD_TARGET}" \
259 |
grep -F 'libraries: =' | cut
-f 2- -d '=' |
tr ':' ';')
260 # We also need our own libraries in the wine path.
261 local real_build_dir
=$
(realpath
"${BUILD_DIR}")
262 # Some library .dll dependencies are installed in /bin:
263 export WINEPATH
="${WINEPATH};${real_build_dir};${real_build_dir}/third_party/brotli;/usr/${BUILD_TARGET}/bin"
265 local prefix
="${BUILD_DIR}/wineprefix"
267 export WINEPREFIX
=$
(realpath
"${prefix}")
269 # Sanitizers need these variables to print and properly format the stack
271 LLVM_SYMBOLIZER
=$
("${CC:-clang}" -print-prog-name=llvm-symbolizer || true
)
272 if [[ -n "${LLVM_SYMBOLIZER}" ]]; then
273 export ASAN_SYMBOLIZER_PATH
="${LLVM_SYMBOLIZER}"
274 export MSAN_SYMBOLIZER_PATH
="${LLVM_SYMBOLIZER}"
275 export UBSAN_SYMBOLIZER_PATH
="${LLVM_SYMBOLIZER}"
282 if [[ "${STACK_SIZE:-0}" == 1 ]]; then
283 # Dump the stack size of each function in the .stack_sizes section for
285 CMAKE_C_FLAGS
+=" -fstack-size-section"
286 CMAKE_CXX_FLAGS
+=" -fstack-size-section"
290 -B"${BUILD_DIR}" -H"${MYDIR}"
291 -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"
293 -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}"
294 -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}"
295 -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}"
296 -DCMAKE_MODULE_LINKER_FLAGS="${CMAKE_MODULE_LINKER_FLAGS}"
297 -DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}"
298 -DJPEGXL_VERSION="${JPEGXL_VERSION}"
299 -DSANITIZER="${SANITIZER}"
300 # These are not enabled by default in cmake.
301 -DJPEGXL_ENABLE_VIEWERS=ON
302 -DJPEGXL_ENABLE_PLUGINS=ON
303 -DJPEGXL_ENABLE_DEVTOOLS=ON
304 # We always use libfuzzer in the ci.sh wrapper.
305 -DJPEGXL_FUZZER_LINK_FLAGS="-fsanitize=fuzzer"
307 if [[ "${BUILD_TARGET}" != *mingw32
]]; then
309 -DJPEGXL_WARNINGS_AS_ERRORS=ON
312 if [[ -n "${BUILD_TARGET}" ]]; then
313 local system_name
="Linux"
314 if [[ "${BUILD_TARGET}" == *mingw32
]]; then
315 # When cross-compiling with mingw the target must be set to Windows and
316 # run programs with wine.
317 system_name
="Windows"
319 -DCMAKE_CROSSCOMPILING_EMULATOR="${WINE_BIN}"
320 # Normally CMake automatically defines MINGW=1 when building with the
321 # mingw compiler (x86_64-w64-mingw32-gcc) but we are normally compiling
326 # EMSCRIPTEN toolchain sets the right values itself
327 if [[ "${BUILD_TARGET}" != wasm
* ]]; then
328 # If set, BUILD_TARGET must be the target triplet such as
329 # x86_64-unknown-linux-gnu.
331 -DCMAKE_C_COMPILER_TARGET="${BUILD_TARGET}"
332 -DCMAKE_CXX_COMPILER_TARGET="${BUILD_TARGET}"
333 # Only the first element of the target triplet.
334 -DCMAKE_SYSTEM_PROCESSOR="${BUILD_TARGET%%-*}"
335 -DCMAKE_SYSTEM_NAME="${system_name}"
336 -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}"
340 # sjpeg confuses WASM SIMD with SSE.
341 -DSJPEG_ENABLE_SIMD=OFF
342 # Building shared libs is not very useful for WASM.
343 -DBUILD_SHARED_LIBS=OFF
347 # These are needed to make googletest work when cross-compiling.
348 -DCMAKE_CROSSCOMPILING=1
351 -DHAVE_GNU_POSIX_REGEX=0
352 -DHAVE_STEADY_CLOCK=0
353 -DHAVE_THREAD_SAFETY_ATTRIBUTES=0
355 if [[ -z "${CMAKE_FIND_ROOT_PATH}" ]]; then
356 # find_package() will look in this prefix for libraries.
357 CMAKE_FIND_ROOT_PATH
="/usr/${BUILD_TARGET}"
359 if [[ -z "${CMAKE_PREFIX_PATH}" ]]; then
360 CMAKE_PREFIX_PATH
="/usr/${BUILD_TARGET}"
362 # Use pkg-config for the target. If there's no pkg-config available for the
363 # target we can set the PKG_CONFIG_PATH to the appropriate path in most
364 # linux distributions.
365 local pkg_config
=$
(which "${BUILD_TARGET}-pkg-config" || true
)
366 if [[ -z "${pkg_config}" ]]; then
367 pkg_config
=$
(which pkg-config
)
368 export PKG_CONFIG_LIBDIR
="/usr/${BUILD_TARGET}/lib/pkgconfig"
370 if [[ -n "${pkg_config}" ]]; then
371 args
+=(-DPKG_CONFIG_EXECUTABLE="${pkg_config}")
374 if [[ -n "${CMAKE_CROSSCOMPILING_EMULATOR}" ]]; then
376 -DCMAKE_CROSSCOMPILING_EMULATOR="${CMAKE_CROSSCOMPILING_EMULATOR}"
379 if [[ -n "${CMAKE_FIND_ROOT_PATH}" ]]; then
381 -DCMAKE_FIND_ROOT_PATH="${CMAKE_FIND_ROOT_PATH}"
384 if [[ -n "${CMAKE_PREFIX_PATH}" ]]; then
386 -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}"
389 if [[ -n "${CMAKE_C_COMPILER_LAUNCHER}" ]]; then
391 -DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_C_COMPILER_LAUNCHER}"
394 if [[ -n "${CMAKE_CXX_COMPILER_LAUNCHER}" ]]; then
396 -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CXX_COMPILER_LAUNCHER}"
399 if [[ -n "${CMAKE_MAKE_PROGRAM}" ]]; then
401 -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}"
404 if [[ "${BUILD_TARGET}" == wasm
* ]]; then
405 emcmake cmake
"${args[@]}" "$@"
407 cmake
"${args[@]}" "$@"
411 cmake_build_and_test
() {
412 if [[ "${SKIP_BUILD}" -eq "1" ]]; then
415 # gtest_discover_tests() runs the test binaries to discover the list of tests
416 # at build time, which fails under qemu.
417 ASAN_OPTIONS
=detect_leaks
=0 cmake
--build "${BUILD_DIR}" -- $TARGETS
418 # Pack test binaries if requested.
419 if [[ "${PACK_TEST:-}" == "1" ]]; then
421 ${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*'
422 # gtest / gtest_main shared libs
423 ${FIND_BIN} lib
/ -name 'libg*.so*'
424 ${FIND_BIN} -type d
-name tests
-a '!' -path '*CMakeFiles*'
425 ) |
tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \
426 --use-compress-program="xz --threads=$(nproc --all || echo 1) -6"
427 du
-h "${BUILD_DIR}/tests.tar.xz"
428 # Pack coverage data if also available.
429 touch "${BUILD_DIR}/gcno.sentinel"
430 (cd "${BUILD_DIR}"; echo gcno.sentinel
; ${FIND_BIN} -name '*gcno') | \
431 tar -C "${BUILD_DIR}" -cvf "${BUILD_DIR}/gcno.tar.xz" -T - \
432 --use-compress-program="xz --threads=$(nproc --all || echo 1) -6"
435 if [[ "${SKIP_TEST}" -ne "1" ]]; then
437 export UBSAN_OPTIONS
=print_stacktrace
=1
438 [[ "${TEST_STACK_LIMIT}" == "none" ]] ||
ulimit -s "${TEST_STACK_LIMIT}"
439 ctest
-j $
(nproc
--all ||
echo 1) ${TEST_SELECTOR} --output-on-failure)
443 # Configure the build to strip unused functions. This considerably reduces the
444 # output size, specially for tests which only use a small part of the whole
447 # Emscripten does tree shaking without any extra flags.
448 if [[ "${BUILD_TARGET}" == wasm
* ]]; then
451 # -ffunction-sections, -fdata-sections and -Wl,--gc-sections effectively
452 # discard all unreachable code, reducing the code size. For this to work, we
453 # need to also pass --no-export-dynamic to prevent it from exporting all the
454 # internal symbols (like functions) making them all reachable and thus not a
455 # candidate for removal.
456 CMAKE_CXX_FLAGS
+=" -ffunction-sections -fdata-sections"
457 CMAKE_C_FLAGS
+=" -ffunction-sections -fdata-sections"
458 if [[ "${OS}" == "Darwin" ]]; then
459 CMAKE_EXE_LINKER_FLAGS
+=" -dead_strip"
460 CMAKE_SHARED_LINKER_FLAGS
+=" -dead_strip"
462 CMAKE_EXE_LINKER_FLAGS
+=" -Wl,--gc-sections -Wl,--no-export-dynamic"
463 CMAKE_SHARED_LINKER_FLAGS
+=" -Wl,--gc-sections -Wl,--no-export-dynamic"
467 ### Externally visible commands
470 CMAKE_BUILD_TYPE
="Debug"
476 CMAKE_BUILD_TYPE
="Release"
483 CMAKE_BUILD_TYPE
="RelWithDebInfo"
484 CMAKE_CXX_FLAGS
+=" -DJXL_DEBUG_WARNING -DJXL_DEBUG_ON_ERROR"
490 # -O0 prohibits stack space reuse -> causes stack-overflow on dozens of tests.
491 TEST_STACK_LIMIT
="none"
493 cmd_release
-DJPEGXL_ENABLE_COVERAGE=ON
"$@"
495 if [[ "${SKIP_TEST}" -ne "1" ]]; then
496 # If we didn't run the test we also don't print a coverage report.
501 cmd_coverage_report
() {
502 LLVM_COV
=$
("${CC:-clang}" -print-prog-name=llvm-cov
)
503 local real_build_dir
=$
(realpath
"${BUILD_DIR}")
505 -r "${real_build_dir}"
506 --gcov-executable "${LLVM_COV} gcov"
507 # Only print coverage information for the libjxl directories. The rest
508 # is not part of the code under test.
510 --exclude '.*_gbench.cc'
511 --exclude '.*_test.cc'
512 --exclude '.*_testonly..*'
513 --exclude '.*_debug.*'
514 --exclude '.*test_utils..*'
515 --object-directory "${real_build_dir}"
519 cd "${real_build_dir}"
520 gcovr
"${gcovr_args[@]}" --html --html-details \
521 --output="${real_build_dir}/coverage.html"
522 gcovr
"${gcovr_args[@]}" --print-summary |
523 tee "${real_build_dir}/coverage.txt"
524 gcovr
"${gcovr_args[@]}" --xml --output="${real_build_dir}/coverage.xml"
530 # Unpack tests if needed.
531 if [[ -e "${BUILD_DIR}/tests.tar.xz" && ! -d "${BUILD_DIR}/tests" ]]; then
532 tar -C "${BUILD_DIR}" -Jxvf "${BUILD_DIR}/tests.tar.xz"
534 if [[ -e "${BUILD_DIR}/gcno.tar.xz" && ! -d "${BUILD_DIR}/gcno.sentinel" ]]; then
535 tar -C "${BUILD_DIR}" -Jxvf "${BUILD_DIR}/gcno.tar.xz"
538 export UBSAN_OPTIONS
=print_stacktrace
=1
539 [[ "${TEST_STACK_LIMIT}" == "none" ]] ||
ulimit -s "${TEST_STACK_LIMIT}"
540 ctest
-j $
(nproc
--all ||
echo 1) ${TEST_SELECTOR} --output-on-failure "$@")
546 export UBSAN_OPTIONS
=print_stacktrace
=1
548 --benchmark_counters_tabular=true \
549 --benchmark_out_format=json \
550 --benchmark_out=gbench.json
"$@"
555 CMAKE_CXX_FLAGS
+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
556 CMAKE_C_FLAGS
+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
557 cmd_asan
-DJPEGXL_ENABLE_FUZZERS=ON
"$@"
561 # Install msan if needed before changing the flags.
563 local msan_prefix
="${HOME}/.msan/${CLANG_VERSION}"
564 if [[ ! -d "${msan_prefix}" ||
-e "${msan_prefix}/lib/libc++abi.a" ]]; then
565 # Install msan libraries for this version if needed or if an older version
566 # with libc++abi was installed.
570 CMAKE_CXX_FLAGS
+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
571 CMAKE_C_FLAGS
+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
572 cmd_msan
-DJPEGXL_ENABLE_FUZZERS=ON
"$@"
577 CMAKE_C_FLAGS
+=" -DJXL_ENABLE_ASSERT=1 -g -DADDRESS_SANITIZER \
578 -fsanitize=address ${UBSAN_FLAGS[@]}"
579 CMAKE_CXX_FLAGS
+=" -DJXL_ENABLE_ASSERT=1 -g -DADDRESS_SANITIZER \
580 -fsanitize=address ${UBSAN_FLAGS[@]}"
582 cmake_configure
"$@" -DJPEGXL_ENABLE_TCMALLOC=OFF
589 -DJXL_ENABLE_ASSERT=1
595 CMAKE_C_FLAGS
+=" ${tsan_args[@]}"
596 CMAKE_CXX_FLAGS
+=" ${tsan_args[@]}"
598 CMAKE_BUILD_TYPE
="RelWithDebInfo"
599 cmake_configure
"$@" -DJPEGXL_ENABLE_TCMALLOC=OFF
606 local msan_prefix
="${HOME}/.msan/${CLANG_VERSION}"
607 if [[ ! -d "${msan_prefix}" ||
-e "${msan_prefix}/lib/libc++abi.a" ]]; then
608 # Install msan libraries for this version if needed or if an older version
609 # with libc++abi was installed.
615 -fno-omit-frame-pointer
617 -DJXL_ENABLE_ASSERT=1
621 # Force gtest to not use the cxxbai.
622 -DGTEST_HAS_CXXABI_H_=0
624 if [[ "${FASTER_MSAN_BUILD}" -ne "1" ]]; then
627 -fsanitize-memory-track-origins
631 local msan_cxx_flags
=(
634 # Some C++ sources don't use the std at all, so the -stdlib=libc++ is unused
635 # in those cases. Ignore the warning.
636 -Wno-unused-command-line-argument
639 # We include the libc++ from the msan directory instead, so we don't want
642 -cxx-isystem"${msan_prefix}/include/c++/v1"
645 local msan_linker_flags
=(
646 -L"${msan_prefix}"/lib
647 -Wl,-rpath -Wl,"${msan_prefix}"/lib
/
650 CMAKE_C_FLAGS
+=" ${msan_c_flags[@]} ${UBSAN_FLAGS[@]}"
651 CMAKE_CXX_FLAGS
+=" ${msan_cxx_flags[@]} ${UBSAN_FLAGS[@]}"
652 CMAKE_EXE_LINKER_FLAGS
+=" ${msan_linker_flags[@]}"
653 CMAKE_MODULE_LINKER_FLAGS
+=" ${msan_linker_flags[@]}"
654 CMAKE_SHARED_LINKER_FLAGS
+=" ${msan_linker_flags[@]}"
656 cmake_configure
"$@" \
657 -DCMAKE_CROSSCOMPILING=1 -DRUN_HAVE_STD_REGEX=0 -DRUN_HAVE_POSIX_REGEX=0 \
658 -DJPEGXL_ENABLE_TCMALLOC=OFF
-DJPEGXL_WARNINGS_AS_ERRORS=OFF \
659 -DCMAKE_REQUIRED_LINK_OPTIONS="${msan_linker_flags[@]}"
663 # Install libc++ libraries compiled with msan in the msan_prefix for the current
666 local tmpdir
=$
(mktemp
-d)
667 CLEANUP_FILES
+=("${tmpdir}")
668 # Detect the llvm to install:
669 export CC
="${CC:-clang}"
670 export CXX
="${CXX:-clang++}"
672 # Allow overriding the LLVM checkout.
673 local llvm_root
="${LLVM_ROOT:-}"
674 if [ -z "${llvm_root}" ]; then
675 local llvm_tag
="llvmorg-${CLANG_VERSION}.0.0"
676 case "${CLANG_VERSION}" in
678 llvm_tag
="llvmorg-6.0.1"
681 llvm_tag
="llvmorg-7.0.1"
684 local llvm_targz
="${tmpdir}/${llvm_tag}.tar.gz"
685 curl
-L --show-error -o "${llvm_targz}" \
686 "https://github.com/llvm/llvm-project/archive/${llvm_tag}.tar.gz"
687 tar -C "${tmpdir}" -zxf "${llvm_targz}"
688 llvm_root
="${tmpdir}/llvm-project-${llvm_tag}"
691 local msan_prefix
="${HOME}/.msan/${CLANG_VERSION}"
692 rm -rf "${msan_prefix}"
695 if [[ -n "${BUILD_TARGET}" ]]; then
697 -DCMAKE_C_COMPILER_TARGET=\"${BUILD_TARGET}\" \
698 -DCMAKE_CXX_COMPILER_TARGET=\"${BUILD_TARGET}\" \
699 -DCMAKE_SYSTEM_PROCESSOR=\"${BUILD_TARGET%%-*}\" \
703 declare -A CMAKE_EXTRAS
704 CMAKE_EXTRAS
[libcxx
]="\
705 -DLIBCXX_CXX_ABI=libstdc++ \
706 -DLIBCXX_INSTALL_EXPERIMENTAL_LIBRARY=ON"
708 for project
in libcxx
; do
709 local proj_build
="${tmpdir}/build-${project}"
710 local proj_dir
="${llvm_root}/${project}"
711 mkdir
-p "${proj_build}"
712 cmake
-B"${proj_build}" -H"${proj_dir}" \
714 -DCMAKE_BUILD_TYPE=Release \
715 -DLLVM_USE_SANITIZER=Memory \
716 -DLLVM_PATH="${llvm_root}/llvm" \
717 -DLLVM_CONFIG_PATH="$(which llvm-config llvm-config-7 llvm-config-6.0 | \
719 -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" \
720 -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" \
721 -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" \
722 -DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}" \
723 -DCMAKE_INSTALL_PREFIX="${msan_prefix}" \
725 ${CMAKE_EXTRAS[${project}]}
726 cmake
--build "${proj_build}"
727 ninja
-C "${proj_build}" install
731 # Internal build step shared between all cmd_ossfuzz_* commands.
735 mkdir
-p "${BUILD_DIR}"
736 local real_build_dir
=$
(realpath
"${BUILD_DIR}")
738 # oss-fuzz defines three directories:
739 # * /work, with the working directory to do re-builds
740 # * /src, with the source code to build
741 # * /out, with the output directory where to copy over the built files.
742 # We use $BUILD_DIR as the /work and the script directory as the /src. The
743 # /out directory is ignored as developers are used to look for the fuzzers in
744 # $BUILD_DIR/tools/ directly.
746 if [[ "${sanitizer}" = "memory" && ! -d "${BUILD_DIR}/msan" ]]; then
747 sudo docker run
--rm -i \
748 --user $
(id
-u):$
(id
-g) \
749 -v "${real_build_dir}":/work \
750 gcr.io
/oss-fuzz-base
/msan-libs-builder \
751 bash
-c "cp -r /msan /work"
754 # Args passed to ninja. These will be evaluated as a string separated by
756 local jpegxl_extra_args
="$@"
758 sudo docker run
--rm -i \
759 -e JPEGXL_UID
=$
(id
-u) \
760 -e JPEGXL_GID
=$
(id
-g) \
761 -e FUZZING_ENGINE
="${FUZZING_ENGINE:-libfuzzer}" \
762 -e SANITIZER
="${sanitizer}" \
763 -e ARCHITECTURE
=x86_64 \
764 -e FUZZING_LANGUAGE
=c
++ \
765 -e MSAN_LIBS_PATH
="/work/msan" \
766 -e JPEGXL_EXTRA_ARGS
="${jpegxl_extra_args}" \
767 -v "${MYDIR}":/src
/libjxl \
768 -v "${MYDIR}/tools/scripts/ossfuzz-build.sh":/src
/build.sh \
769 -v "${real_build_dir}":/work \
770 gcr.io
/oss-fuzz
/libjxl
774 _cmd_ossfuzz address
"$@"
777 _cmd_ossfuzz memory
"$@"
779 cmd_ossfuzz_ubsan
() {
780 _cmd_ossfuzz undefined
"$@"
783 cmd_ossfuzz_ninja
() {
784 [[ -e "${BUILD_DIR}/build.ninja" ]]
785 local real_build_dir
=$
(realpath
"${BUILD_DIR}")
787 if [[ -e "${BUILD_DIR}/msan" ]]; then
788 echo "ossfuzz_ninja doesn't work with msan builds. Use ossfuzz_msan." >&2
792 sudo docker run
--rm -i \
793 --user $
(id
-u):$
(id
-g) \
794 -v "${MYDIR}":/src
/libjxl \
795 -v "${real_build_dir}":/work \
796 gcr.io
/oss-fuzz
/libjxl \
800 cmd_fast_benchmark
() {
801 local small_corpus_tar
="${BENCHMARK_CORPORA}/jyrki-full.tar"
802 local small_corpus_url
="https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar"
803 mkdir
-p "${BENCHMARK_CORPORA}"
804 if [ -f "${small_corpus_tar}" ]; then
805 curl
--show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" "${small_corpus_url}"
807 curl
--show-error -o "${small_corpus_tar}" "${small_corpus_url}"
810 local tmpdir
=$
(mktemp
-d)
811 CLEANUP_FILES
+=("${tmpdir}")
812 tar -xf "${small_corpus_tar}" -C "${tmpdir}"
814 run_benchmark
"${tmpdir}" 1048576
818 local nikon_corpus_tar
="${BENCHMARK_CORPORA}/nikon-subset.tar"
819 mkdir
-p "${BENCHMARK_CORPORA}"
820 curl
--show-error -o "${nikon_corpus_tar}" -z "${nikon_corpus_tar}" \
821 "https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/nikon-subset.tar"
823 local tmpdir
=$
(mktemp
-d)
824 CLEANUP_FILES
+=("${tmpdir}")
825 tar -xvf "${nikon_corpus_tar}" -C "${tmpdir}"
827 local sem_id
="jpegxl_benchmark-$$"
828 local nprocs
=$
(nproc
--all ||
echo 1)
831 while IFS
= read -r filename
; do
832 # This removes the './'
833 filename
="${filename:2}"
835 if [[ "${filename:0:4}" == "srgb" ]]; then
836 mode
="RGB_D65_SRG_Rel_SRG"
837 elif [[ "${filename:0:5}" == "adobe" ]]; then
838 mode
="RGB_D65_Ado_Rel_Ado"
840 echo "Unknown image colorspace: ${filename}" >&2
843 png_filename
="${filename%.ppm}.png"
844 png_filename
=$
(echo "${png_filename}" |
tr '/' '_')
845 sem
--bg --id "${sem_id}" -j"${nprocs}" -- \
846 "${TOOLS_DIR}/decode_and_encode" \
847 "${tmpdir}/${filename}" "${mode}" "${tmpdir}/${png_filename}"
848 images
+=( "${png_filename}" )
849 done < <(cd "${tmpdir}"; ${FIND_BIN} .
-name '*.ppm' -type f
)
850 sem
--id "${sem_id}" --wait
852 # We need about 10 GiB per thread on these images.
853 run_benchmark
"${tmpdir}" 10485760
856 get_mem_available
() {
857 if [[ "${OS}" == "Darwin" ]]; then
858 echo $
(vm_stat |
grep -F 'Pages free:' |
awk '{print $3 * 4}')
859 elif [[ "${OS}" == MINGW
* ]]; then
860 echo $
(vmstat |
tail -n 1 |
awk '{print $4 * 4}')
862 echo $
(grep -F MemAvailable
: /proc
/meminfo |
awk '{print $2}')
867 local src_img_dir
="$1"
868 local mem_per_thread
="${2:-10485760}"
870 local output_dir
="${BUILD_DIR}/benchmark_results"
871 mkdir
-p "${output_dir}"
873 if [[ "${OS}" == MINGW
* ]]; then
874 src_img_dir
=`cygpath -w "${src_img_dir}"`
878 if [[ ${BENCHMARK_NUM_THREADS} -gt 0 ]]; then
879 num_threads
=${BENCHMARK_NUM_THREADS}
881 # The memory available at the beginning of the benchmark run in kB. The number
882 # of threads depends on the available memory, and the passed memory per
883 # thread. We also add a 2 GiB of constant memory.
884 local mem_available
="$(get_mem_available)"
885 # Check that we actually have a MemAvailable value.
886 [[ -n "${mem_available}" ]]
887 num_threads
=$
(( (${mem_available} - 1048576) / ${mem_per_thread} ))
888 if [[ ${num_threads} -le 0 ]]; then
893 local benchmark_args
=(
894 --input "${src_img_dir}/*.png"
895 --codec=jpeg
:yuv420
:q85
,webp
:q80
,jxl
:d1
:6,jxl
:d1
:6:downsampling
=8,jxl
:d5
:6,jxl
:d5
:6:downsampling
=8,jxl
:m
:d0
:2,jxl
:m
:d0
:3,jxl
:m
:d2
:2
896 --output_dir "${output_dir}"
898 --num_threads="${num_threads}"
902 if [[ "${STORE_IMAGES}" == "1" ]]; then
903 benchmark_args
+=(--save_decompressed --save_compressed)
906 [[ "${TEST_STACK_LIMIT}" == "none" ]] ||
ulimit -s "${TEST_STACK_LIMIT}"
907 "${TOOLS_DIR}/benchmark_xl" "${benchmark_args[@]}" | \
908 tee "${output_dir}/results.txt"
910 # Check error code for benckmark_xl command. This will exit if not.
911 return ${PIPESTATUS[0]}
915 # Helper function to wait for the CPU temperature to cool down on ARM.
917 { set +x
; } 2>/dev
/null
918 local temp_limit
=${1:-38000}
919 if [[ -z "${THERMAL_FILE:-}" ]]; then
920 echo "Must define the THERMAL_FILE with the thermal_zoneX/temp file" \
921 "to read the temperature from. This is normally set in the runner." >&2
924 local org_temp
=$
(cat "${THERMAL_FILE}")
925 if [[ "${org_temp}" -ge "${temp_limit}" ]]; then
926 echo -n "Waiting for temp to get down from ${org_temp}... "
928 local temp
="${org_temp}"
930 while [[ "${temp}" -ge "${temp_limit}" ]]; do
932 temp
=$
(cat "${THERMAL_FILE}")
935 if [[ ${secs} -ge 5 ]]; then
939 if [[ "${org_temp}" -ge "${temp_limit}" ]]; then
940 echo "Done, temp=${temp}"
945 # Helper function to set the cpuset restriction of the current process.
947 [[ "${SKIP_CPUSET:-}" != "1" ]] ||
return 0
949 local mycpuset
=$
(cat /proc
/self
/cpuset
)
950 mycpuset
="/dev/cpuset${mycpuset}"
951 # Check that the directory exists:
952 [[ -d "${mycpuset}" ]]
953 if [[ -e "${mycpuset}/cpuset.cpus" ]]; then
954 echo "${newset}" >"${mycpuset}/cpuset.cpus"
956 echo "${newset}" >"${mycpuset}/cpus"
960 # Return the encoding/decoding speed from the Stats output.
961 _speed_from_output
() {
963 local unit
="${2:-MP/s}"
964 if [[ "${speed}" == *"${unit}"* ]]; then
965 speed
="${speed%% ${unit}*}"
972 # Run benchmarks on ARM for the big and little CPUs.
973 cmd_arm_benchmark
() {
974 # Flags used for cjxl encoder with .png inputs
975 local jxl_png_benchmarks
=(
977 "--epf=0 --distance=1.0 --speed=cheetah"
978 "--epf=2 --distance=1.0 --speed=cheetah"
979 "--epf=0 --distance=8.0 --speed=cheetah"
980 "--epf=1 --distance=8.0 --speed=cheetah"
981 "--epf=2 --distance=8.0 --speed=cheetah"
982 "--epf=3 --distance=8.0 --speed=cheetah"
987 "--modular -E 0 -I 0"
989 "--modular --responsive=1"
990 # Near-lossless options:
991 "--epf=0 --distance=0.3 --speed=fast"
995 # Flags used for cjxl encoder with .jpg inputs. These should do lossless
996 # JPEG recompression (of pixels or full jpeg).
997 local jxl_jpeg_benchmarks
=(
1002 "testdata/jxl/flower/flower.png"
1006 "testdata/jxl/flower/flower.png.im_q85_420.jpg"
1009 if [[ "${SKIP_CPUSET:-}" == "1" ]]; then
1010 # Use a single cpu config in this case.
1011 local cpu_confs
=("?")
1013 # Otherwise the CPU config comes from the environment:
1015 "${RUNNER_CPU_LITTLE}"
1017 # The CPU description is something like 3-7, so these configurations only
1018 # take the first CPU of the group.
1019 "${RUNNER_CPU_LITTLE%%-*}"
1020 "${RUNNER_CPU_BIG%%-*}"
1022 # Check that RUNNER_CPU_ALL is defined. In the SKIP_CPUSET=1 case this will
1023 # be ignored but still evaluated when calling cmd_cpuset.
1024 [[ -n "${RUNNER_CPU_ALL}" ]]
1027 local jpg_dirname
="third_party/corpora/jpeg"
1028 mkdir
-p "${jpg_dirname}"
1029 local jpg_qualities
=( 50 80 95 )
1030 for src_img
in "${images[@]}"; do
1031 for q
in "${jpg_qualities[@]}"; do
1032 local jpeg_name
="${jpg_dirname}/"$(basename "${src_img}" .png)"-q${q}.jpg"
1033 convert
-sampling-factor 1x1
-quality "${q}" \
1034 "${src_img}" "${jpeg_name}"
1035 jpg_images
+=("${jpeg_name}")
1039 local output_dir
="${BUILD_DIR}/benchmark_results"
1040 mkdir
-p "${output_dir}"
1041 local runs_file
="${output_dir}/runs.txt"
1043 if [[ ! -e "${runs_file}" ]]; then
1044 echo -e "binary\tflags\tsrc_img\tsrc size\tsrc pixels\tcpuset\tenc size (B)\tenc speed (MP/s)\tdec speed (MP/s)\tJPG dec speed (MP/s)\tJPG dec speed (MB/s)" |
1045 tee -a "${runs_file}"
1048 mkdir
-p "${BUILD_DIR}/arm_benchmark"
1051 for src_img
in "${jpg_images[@]}" "${images[@]}"; do
1052 local src_img_hash
=$
(sha1sum "${src_img}" | cut
-f 1 -d ' ')
1053 local enc_binaries
=("${TOOLS_DIR}/cjxl")
1054 local src_ext
="${src_img##*.}"
1055 for enc_binary
in "${enc_binaries[@]}"; do
1056 local enc_binary_base
=$
(basename "${enc_binary}")
1058 # Select the list of flags to use for the current encoder/image pair.
1059 local img_benchmarks
1060 if [[ "${src_ext}" == "jpg" ]]; then
1061 img_benchmarks
=("${jxl_jpeg_benchmarks[@]}")
1063 img_benchmarks
=("${jxl_png_benchmarks[@]}")
1066 for flags
in "${img_benchmarks[@]}"; do
1068 local enc_file_hash
="${enc_binary_base} || $flags || ${src_img} || ${src_img_hash}"
1069 enc_file_hash
=$
(echo "${enc_file_hash}" |
sha1sum | cut
-f 1 -d ' ')
1070 local enc_file
="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jxl"
1072 for cpu_conf
in "${cpu_confs[@]}"; do
1073 cmd_cpuset
"${cpu_conf}"
1074 # nproc returns the number of active CPUs, which is given by the cpuset
1076 local num_threads
="$(nproc)"
1078 echo "Encoding with: ${enc_binary_base} img=${src_img} cpus=${cpu_conf} enc_flags=${flags}"
1080 if [[ "${flags}" == *"modular"* ]]; then
1081 # We don't benchmark encoding speed in this case.
1082 if [[ ! -f "${enc_file}" ]]; then
1083 cmd_cpuset
"${RUNNER_CPU_ALL:-}"
1084 "${enc_binary}" ${flags} "${src_img}" "${enc_file}.tmp"
1085 mv "${enc_file}.tmp" "${enc_file}"
1086 cmd_cpuset
"${cpu_conf}"
1088 enc_output
=" ?? MP/s"
1091 enc_output
=$
("${enc_binary}" ${flags} "${src_img}" "${enc_file}.tmp" \
1092 2>&1 |
tee /dev
/stderr |
grep -F "MP/s [")
1093 mv "${enc_file}.tmp" "${enc_file}"
1095 local enc_speed
=$
(_speed_from_output
"${enc_output}")
1096 local enc_size
=$
(stat
-c "%s" "${enc_file}")
1098 echo "Decoding with: img=${src_img} cpus=${cpu_conf} enc_flags=${flags}"
1102 dec_output
=$
("${TOOLS_DIR}/djxl" "${enc_file}" \
1103 --num_reps=5 --num_threads="${num_threads}" 2>&1 |
tee /dev
/stderr |
1104 grep -E "M[BP]/s \[")
1105 local img_size
=$
(echo "${dec_output}" | cut
-f 1 -d ',')
1106 local img_size_x
=$
(echo "${img_size}" | cut
-f 1 -d ' ')
1107 local img_size_y
=$
(echo "${img_size}" | cut
-f 3 -d ' ')
1108 local img_size_px
=$
(( ${img_size_x} * ${img_size_y} ))
1109 local dec_speed
=$
(_speed_from_output
"${dec_output}")
1111 # For JPEG lossless recompression modes (where the original is a JPEG)
1112 # decode to JPG as well.
1113 local jpeg_dec_mps_speed
=""
1114 local jpeg_dec_mbs_speed
=""
1115 if [[ "${src_ext}" == "jpg" ]]; then
1117 local dec_file
="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jpg"
1118 dec_output
=$
("${TOOLS_DIR}/djxl" "${enc_file}" \
1119 "${dec_file}" --num_reps=5 --num_threads="${num_threads}" 2>&1 | \
1120 tee /dev
/stderr |
grep -E "M[BP]/s \[")
1121 local jpeg_dec_mps_speed
=$
(_speed_from_output
"${dec_output}")
1122 local jpeg_dec_mbs_speed
=$
(_speed_from_output
"${dec_output}" MB
/s
)
1123 if ! cmp --quiet "${src_img}" "${dec_file}"; then
1124 # Add a start at the end to signal that the files are different.
1125 jpeg_dec_mbs_speed
+="*"
1129 # Record entry in a tab-separated file.
1130 local src_img_base
=$
(basename "${src_img}")
1131 echo -e "${enc_binary_base}\t${flags}\t${src_img_base}\t${img_size}\t${img_size_px}\t${cpu_conf}\t${enc_size}\t${enc_speed}\t${dec_speed}\t${jpeg_dec_mps_speed}\t${jpeg_dec_mbs_speed}" |
1132 tee -a "${runs_file}"
1137 cmd_cpuset
"${RUNNER_CPU_ALL:-}"
1142 # Generate a corpus and run the fuzzer on that corpus.
1144 local corpus_dir
=$
(realpath
"${BUILD_DIR}/fuzzer_corpus")
1145 local fuzzer_crash_dir
=$
(realpath
"${BUILD_DIR}/fuzzer_crash")
1146 mkdir
-p "${corpus_dir}" "${fuzzer_crash_dir}"
1148 "${TOOLS_DIR}/fuzzer_corpus" "${corpus_dir}"
1150 local nprocs
=$
(nproc
--all ||
echo 1)
1153 djxl_fuzzer
"${fuzzer_crash_dir}" "${corpus_dir}" \
1154 -max_total_time="${FUZZER_MAX_TIME}" -jobs=${nprocs} \
1155 -artifact_prefix="${fuzzer_crash_dir}/"
1159 # Runs the linters (clang-format, build_cleaner, buildirier) on the pending CLs.
1161 merge_request_commits
1162 { set +x
; } 2>/dev
/null
1163 local versions
=(${1:-16 15 14 13 12 11 10 9 8 7 6.0})
1164 local clang_format_bins
=("${versions[@]/#/clang-format-}" clang-format
)
1165 local tmpdir
=$
(mktemp
-d)
1166 CLEANUP_FILES
+=("${tmpdir}")
1169 local build_patch
="${tmpdir}/build_cleaner.patch"
1170 if ! "${MYDIR}/tools/scripts/build_cleaner.py" >"${build_patch}"; then
1172 echo "build_cleaner.py findings:" >&2
1173 "${COLORDIFF_BIN}" <"${build_patch}"
1174 echo "Run \`tools/scripts/build_cleaner.py --update\` to apply them" >&2
1177 # It is ok, if buildifier is not installed.
1178 if which buildifier
>/dev
/null
; then
1179 local buildifier_patch
="${tmpdir}/buildifier.patch"
1180 local bazel_files
=`git -C ${MYDIR} ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"`
1182 buildifier
-d ${bazel_files} >"${buildifier_patch}"|| true
1183 { set +x
; } 2>/dev
/null
1184 if [ -s "${buildifier_patch}" ]; then
1186 echo 'buildifier have found some problems in Bazel build files:' >&2
1187 "${COLORDIFF_BIN}" <"${buildifier_patch}"
1188 echo 'To fix them run (from the base directory):' >&2
1189 echo ' buildifier `git ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"`' >&2
1196 for clang_format
in "${clang_format_bins[@]}"; do
1197 if ! which "${clang_format}" >/dev
/null
; then
1200 installed
+=("${clang_format}")
1201 local tmppatch
="${tmpdir}/${clang_format}.patch"
1202 # We include in this linter all the changes including the uncommitted changes
1203 # to avoid printing changes already applied.
1205 # Ignoring the error that git-clang-format outputs.
1206 git
-C "${MYDIR}" "${clang_format}" --binary "${clang_format}" \
1207 --style=file --diff "${MR_ANCESTOR_SHA}" -- >"${tmppatch}" || true
1208 { set +x
; } 2>/dev
/null
1209 if grep -E '^--- ' "${tmppatch}" |
grep -v 'a/third_party' >/dev
/null
; then
1210 if [[ -n "${LINT_OUTPUT:-}" ]]; then
1211 cp "${tmppatch}" "${LINT_OUTPUT}"
1213 clang_patch
="${tmppatch}"
1215 echo "clang-format check OK" >&2
1220 if [[ ${#installed[@]} -eq 0 ]]; then
1221 echo "You must install clang-format for \"git clang-format\"" >&2
1225 # clang-format is installed but found problems.
1226 echo "clang-format findings:" >&2
1227 "${COLORDIFF_BIN}" < "${clang_patch}"
1229 echo "clang-format found issues in your patches from ${MR_ANCESTOR_SHA}" \
1230 "to the current patch. Run \`./ci.sh lint | patch -p1\` from the base" \
1231 "directory to apply them." >&2
1235 # Runs clang-tidy on the pending CLs. If the "all" argument is passed it runs
1236 # clang-tidy over all the source files instead.
1240 if [[ -z "${CLANG_TIDY_BIN}" ]]; then
1241 echo "ERROR: You must install clang-tidy-7 or newer to use ci.sh tidy" >&2
1246 if [[ "${what}" == "all" ]]; then
1250 merge_request_commits
1252 diff-tree
--no-commit-id --name-only -r "${MR_ANCESTOR_SHA}"
1257 # Clang-tidy needs the compilation database generated by cmake.
1258 if [[ ! -e "${BUILD_DIR}/compile_commands.json" ]]; then
1259 # Generate the build options in debug mode, since we need the debug asserts
1260 # enabled for the clang-tidy analyzer to use them.
1261 CMAKE_BUILD_TYPE
="Debug"
1263 # Build the autogen targets to generate the .h files from the .ui files.
1264 local autogen_targets
=(
1265 $
(ninja
-C "${BUILD_DIR}" -t targets |
grep -F _autogen
: |
1268 if [[ ${#autogen_targets[@]} != 0 ]]; then
1269 ninja
-C "${BUILD_DIR}" "${autogen_targets[@]}"
1274 local nprocs
=$
(nproc
--all ||
echo 1)
1276 if ! parallel
-j"${nprocs}" --keep-order -- \
1277 "${CLANG_TIDY_BIN}" -p "${BUILD_DIR}" -format-style=file -quiet "$@" {} \
1278 < <(git "${git_args[@]}" | grep -E '(\.cc|\.cpp)$') \
1279 >"${BUILD_DIR}/clang-tidy.txt
"; then
1282 { set +x; } 2>/dev/null
1283 echo "Findings statistics
:" >&2
1284 grep -E ' \[[A-Za-z\.,\-]+\]' -o "${BUILD_DIR}/clang-tidy.txt
" | sort \
1287 if [[ $ret -ne 0 ]]; then
1289 Errors found, see ${BUILD_DIR}/clang-tidy.txt for details.
1290 To automatically fix them, run:
1292 SKIP_TEST=1 ./ci.sh debug
1293 ${CLANG_TIDY_BIN} -p ${BUILD_DIR} -fix -format-style=file -quiet $@ \$(git ${git_args[@]} | grep -E '(\.cc|\.cpp)\$')
1300 # Print stats about all the packages built in ${BUILD_DIR}/debs/.
1301 cmd_debian_stats() {
1302 { set +x; } 2>/dev/null
1303 local debsdir="${BUILD_DIR}/debs
"
1305 while IFS='' read -r -d '' f; do
1306 echo "====================================================================="
1310 done < <(find "${BUILD_DIR}/debs
" -maxdepth 1 -mindepth 1 -type f \
1311 -name '*.deb' -print0)
1314 build_debian_pkg() {
1317 local options="${3:-}"
1319 local debsdir="${BUILD_DIR}/debs
"
1320 local builddir="${debsdir}/${srcpkg}"
1322 # debuild doesn't have an easy way to build out of tree, so we make a copy
1323 # of with all symlinks on the first level.
1324 mkdir -p "${builddir}"
1325 for f in $(find "${srcdir}" -mindepth 1 -maxdepth 1 -printf '%P\n'); do
1326 if [[ ! -L "${builddir}/$f" ]]; then
1327 rm -f "${builddir}/$f"
1328 ln -s "${srcdir}/$f" "${builddir}/$f"
1333 debuild "${options}" -b -uc -us
1337 cmd_debian_build() {
1338 local srcpkg="${1:-}"
1342 build_debian_pkg "${MYDIR}" "jpeg-xl
"
1345 build_debian_pkg "${MYDIR}/third_party
/highway
" "highway
" "${HWY_PKG_OPTIONS}"
1348 echo "ERROR
: Must pass a valid
source package name to build.
" >&2
1355 local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1)
1357 line="${line#set(${varname} }"
1362 cmd_bump_version() {
1363 local newver="${1:-}"
1365 if ! which dch >/dev/null; then
1366 echo "Missing dch
\nTo
install it run
:\n sudo apt
install devscripts
"
1370 if [[ -z "${newver}" ]]; then
1371 local major=$(get_version JPEGXL_MAJOR_VERSION)
1372 local minor=$(get_version JPEGXL_MINOR_VERSION)
1374 minor=$(( ${minor} + 1))
1376 local major="${newver%%.*}"
1377 newver="${newver#*.}"
1378 local minor="${newver%%.*}"
1379 newver="${newver#${minor}}"
1380 local patch="${newver#.}"
1381 if [[ -z "${patch}" ]]; then
1386 newver="${major}.${minor}.${patch}"
1388 echo "Bumping version to
${newver} (${major}.${minor}.${patch})"
1390 -e "s
/(set\\(JPEGXL_MAJOR_VERSION
) [0-9]+\\)/\\1 ${major})/" \
1391 -e "s
/(set\\(JPEGXL_MINOR_VERSION
) [0-9]+\\)/\\1 ${minor})/" \
1392 -e "s
/(set\\(JPEGXL_PATCH_VERSION
) [0-9]+\\)/\\1 ${patch})/" \
1393 -i lib/CMakeLists.txt
1395 -e "s
/(LIBJXL_VERSION
: )\"[0-9.
]+\"/\\1\"${major}.${minor}.${patch}\"/" \
1396 -e "s
/(LIBJXL_ABI_VERSION
: )\"[0-9.
]+\"/\\1\"${major}.
${minor}\"/" \
1397 -i .github/workflows/conformance.yml
1400 tools/scripts/build_cleaner.py --update
1402 # Mark the previous version as "unstable
".
1403 DEBCHANGE_RELEASE_HEURISTIC=log dch -M --distribution unstable --release ''
1404 DEBCHANGE_RELEASE_HEURISTIC=log dch -M \
1405 --newversion "${newver}" \
1406 "Bump JPEG XL version to
${newver}.
"
1409 # Check that the AUTHORS file contains the email of the committer.
1411 merge_request_commits
1414 readarray -t emails < <(git log --format='%ae' "${MR_ANCESTOR_SHA}..
${MR_HEAD_SHA}")
1415 readarray -t names < <(git log --format='%an' "${MR_ANCESTOR_SHA}..
${MR_HEAD_SHA}")
1416 for i in "${!names[@]}"; do
1417 echo "Checking name
'${names[$i]}' with email
'${emails[$i]}' ...
"
1418 "${MYDIR}"/tools/scripts/check_author.py "${emails[$i]}" "${names[$i]}"
1424 if [[ -z "${cmd}" ]]; then
1428 Where cmd is one of:
1429 opt Build and test a Release with symbols build.
1430 debug Build and test a Debug build (NDEBUG is not defined).
1431 release Build and test a striped Release binary without debug information.
1432 asan Build and test an ASan (AddressSanitizer) build.
1433 msan Build and test an MSan (MemorySanitizer) build. Needs to have msan
1434 c++ libs installed with msan_install first.
1435 tsan Build and test a TSan (ThreadSanitizer) build.
1436 asanfuzz Build and test an ASan (AddressSanitizer) build for fuzzing.
1437 msanfuzz Build and test an MSan (MemorySanitizer) build for fuzzing.
1438 test Run the tests build by opt, debug, release, asan or msan. Useful when
1439 building with SKIP_TEST=1.
1440 gbench Run the Google benchmark tests.
1441 fuzz Generate the fuzzer corpus and run the fuzzer on it. Useful after
1442 building with asan or msan.
1443 benchmark Run the benchmark over the default corpus.
1444 fast_benchmark Run the benchmark over the small corpus.
1446 coverage Build and run tests with coverage support. Runs coverage_report as
1448 coverage_report Generate HTML, XML and text coverage report after a coverage
1451 lint Run the linter checks on the current commit or merge request.
1452 tidy Run clang-tidy on the current commit or merge request.
1453 authors Check that the last commit's author is listed in the AUTHORS file.
1455 msan_install Install the libc++ libraries required to build in msan mode. This
1456 needs to be done once.
1458 debian_build <srcpkg> Build the given source package.
1459 debian_stats Print stats about the built packages.
1462 ossfuzz_asan Build the local source inside oss-fuzz docker with asan.
1463 ossfuzz_msan Build the local source inside oss-fuzz docker with msan.
1464 ossfuzz_ubsan Build the local source inside oss-fuzz docker with ubsan.
1465 ossfuzz_ninja Run ninja on the BUILD_DIR inside the oss-fuzz docker. Extra
1466 parameters are passed to ninja, for example "djxl_fuzzer
" will
1467 only build that ninja target. Use for faster build iteration
1468 after one of the ossfuzz_*san commands.
1470 You can pass some optional environment variables as well:
1471 - BUILD_DIR: The output build directory (by default "$
$repo/build
")
1472 - BUILD_TARGET: The target triplet used when cross-compiling.
1473 - CMAKE_FLAGS: Convenience flag to pass both CMAKE_C_FLAGS and CMAKE_CXX_FLAGS.
1474 - CMAKE_PREFIX_PATH: Installation prefixes to be searched by the find_package.
1475 - ENABLE_WASM_SIMD=1: enable experimental SIMD in WASM build (only).
1476 - FUZZER_MAX_TIME: "fuzz
" command fuzzer running timeout in seconds.
1477 - LINT_OUTPUT: Path to the output patch from the "lint
" command.
1478 - SKIP_CPUSET=1: Skip modifying the cpuset in the arm_benchmark.
1479 - SKIP_BUILD=1: Skip the build stage, cmake configure only.
1480 - SKIP_TEST=1: Skip the test stage.
1481 - STORE_IMAGES=0: Makes the benchmark discard the computed images.
1482 - TEST_STACK_LIMIT: Stack size limit (ulimit -s) during tests, in KiB.
1483 - TEST_SELECTOR: pass additional arguments to ctest, e.g. "-R .Resample.
".
1484 - STACK_SIZE=1: Generate binaries with the .stack_sizes sections.
1486 These optional environment variables are forwarded to the cmake call as
1491 - CMAKE_C_COMPILER_LAUNCHER
1492 - CMAKE_CXX_COMPILER_LAUNCHER
1493 - CMAKE_CROSSCOMPILING_EMULATOR
1494 - CMAKE_FIND_ROOT_PATH
1495 - CMAKE_EXE_LINKER_FLAGS
1496 - CMAKE_MAKE_PROGRAM
1497 - CMAKE_MODULE_LINKER_FLAGS
1498 - CMAKE_SHARED_LINKER_FLAGS
1499 - CMAKE_TOOLCHAIN_FILE
1502 BUILD_DIR=/tmp/build $0 opt