From 5524e5f470832e18b5ee0b78e897dd2fc573592d Mon Sep 17 00:00:00 2001
From: Vicente Adolfo Bolea Sanchez <vicente.bolea@kitware.com>
Date: Tue, 26 Nov 2024 20:59:07 -0500
Subject: [PATCH] ci: rehaul sanitizer builds

- Based them on opensuse tumbleweed to get recent
  versions of their packages.
- Update clang/llvm to 17
- Add build-images-opensuse-tw script
---
 .gitlab/gitlab-ci-spack.yml                   |   6 +-
 scripts/ci/cmake/ci-uo-sanitizer-asan.cmake   |   9 ++
 scripts/ci/cmake/ci-uo-sanitizer-msan.cmake   |   5 +-
 scripts/ci/cmake/ci-uo-sanitizer-tsan.cmake   |   8 ++
 scripts/ci/cmake/ci-uo-sanitizer-ubsan.cmake  |   8 ++
 .../opensuse-tw/build-images-opensuse-tw      |  49 +++++++
 .../opensuse-tw/opensuse-tw-asan.dockerfile   |  20 +++
 .../opensuse-tw-full-stack-onbuild.dockerfile | 125 ++++++++++++++++++
 .../opensuse-tw/opensuse-tw-msan.dockerfile   |   9 ++
 .../opensuse-tw-sanitizer-base.dockerfile     |  33 +++++
 .../opensuse-tw/opensuse-tw-tsan.dockerfile   |   9 ++
 .../opensuse-tw/opensuse-tw-ubsan.dockerfile  |   5 +
 .../images/opensuse-tw/toolchain-msan.cmake   |   9 ++
 .../images/opensuse-tw/toolchain-tsan.cmake   |   9 ++
 14 files changed, 297 insertions(+), 7 deletions(-)
 create mode 100755 scripts/ci/images/opensuse-tw/build-images-opensuse-tw
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-asan.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-full-stack-onbuild.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-msan.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-sanitizer-base.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-tsan.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/opensuse-tw-ubsan.dockerfile
 create mode 100644 scripts/ci/images/opensuse-tw/toolchain-msan.cmake
 create mode 100644 scripts/ci/images/opensuse-tw/toolchain-tsan.cmake

diff --git a/.gitlab/gitlab-ci-spack.yml b/.gitlab/gitlab-ci-spack.yml
index c042c7f243..ab7f9e408d 100644
--- a/.gitlab/gitlab-ci-spack.yml
+++ b/.gitlab/gitlab-ci-spack.yml
@@ -7,11 +7,6 @@ default:
     - public
     - x86_64
 
-.common-sanitizer:
-  variables:
-    GITLAB_SITE: "UO CI (gitlab.spack.io)"
-    CI_BIN_DIR:  "$CI_PROJECT_DIR/build"
-
 #  This file is used to define the GitLab CI/CD pipeline for the ADIOS2 project.
 .common-sanitizer:
   rules:
@@ -41,6 +36,7 @@ default:
       - $CI_BIN_DIR/testing/
       - $CI_BIN_DIR/source/**/*.h
       - $CI_BIN_DIR/source/**/*.in
+      - $CI_BIN_DIR/adios2_reorganize_wrapper
 
       # CTest and CMake install files.
       - $CI_BIN_DIR/CMakeCache.txt
diff --git a/scripts/ci/cmake/ci-uo-sanitizer-asan.cmake b/scripts/ci/cmake/ci-uo-sanitizer-asan.cmake
index 536e5e358e..c56a0e319a 100644
--- a/scripts/ci/cmake/ci-uo-sanitizer-asan.cmake
+++ b/scripts/ci/cmake/ci-uo-sanitizer-asan.cmake
@@ -8,6 +8,8 @@ set(ENV{LSAN_OPTIONS} "suppressions=$ENV{CI_SOURCE_DIR}/scripts/ci/cmake/adios-a
 set(ENV{CFLAGS}   "${ASAN_FLAGS}")
 set(ENV{CXXFLAGS} "${ASAN_FLAGS}")
 
+set(CTEST_MEMORYCHECK_SANITIZER_OPTIONS "detect_odr_violation=0")
+
 set(dashboard_cache "
 BUILD_TESTING:BOOL=ON
 ADIOS2_BUILD_EXAMPLES:BOOL=ON
@@ -23,6 +25,13 @@ set(dashboard_track "Analysis")
 set(CTEST_CMAKE_GENERATOR "Ninja")
 set(CTEST_MEMORYCHECK_TYPE "AddressSanitizer")
 
+list(APPEND EXCLUDE_EXPRESSIONS
+  "Install.*"
+  "Unit.FileTransport.FailOnEOF.Serial"
+  )
+list(JOIN EXCLUDE_EXPRESSIONS "|" TEST_EXCLUDE_STRING)
+set(CTEST_MEMCHECK_ARGS EXCLUDE "${TEST_EXCLUDE_STRING}")
+
 set(ADIOS_TEST_REPEAT 0)
 list(APPEND CTEST_UPDATE_NOTES_FILES "${CMAKE_CURRENT_LIST_FILE}")
 include(${CMAKE_CURRENT_LIST_DIR}/ci-common.cmake)
diff --git a/scripts/ci/cmake/ci-uo-sanitizer-msan.cmake b/scripts/ci/cmake/ci-uo-sanitizer-msan.cmake
index b6204e84c6..2e60d97f22 100644
--- a/scripts/ci/cmake/ci-uo-sanitizer-msan.cmake
+++ b/scripts/ci/cmake/ci-uo-sanitizer-msan.cmake
@@ -22,8 +22,9 @@ set(CTEST_CMAKE_GENERATOR "Ninja")
 set(CTEST_MEMORYCHECK_TYPE "MemorySanitizer")
 
 list(APPEND EXCLUDE_EXPRESSIONS
-  "Engine.BP.BPBufferSizeTest.SyncDeferredIdenticalUsage.BP3.Serial"
-  "Engine.BP.BPBufferSizeTest.SyncDeferredIdenticalUsage.BP4.Serial"
+  "Install.*"
+  "Unit.FileTransport.FailOnEOF.Serial"
+  "Engine.BP.BPBufferSizeTest.SyncDeferredIdenticalUsage.*.Serial"
   )
 list(JOIN EXCLUDE_EXPRESSIONS "|" TEST_EXCLUDE_STRING)
 set(CTEST_MEMCHECK_ARGS EXCLUDE "${TEST_EXCLUDE_STRING}")
diff --git a/scripts/ci/cmake/ci-uo-sanitizer-tsan.cmake b/scripts/ci/cmake/ci-uo-sanitizer-tsan.cmake
index 84c4d73cf5..412c62cd66 100644
--- a/scripts/ci/cmake/ci-uo-sanitizer-tsan.cmake
+++ b/scripts/ci/cmake/ci-uo-sanitizer-tsan.cmake
@@ -18,6 +18,14 @@ set(dashboard_track "Analysis")
 set(CTEST_CMAKE_GENERATOR "Ninja")
 set(CTEST_MEMORYCHECK_TYPE "ThreadSanitizer")
 
+list(APPEND EXCLUDE_EXPRESSIONS
+  "Install.*"
+  "Unit.FileTransport.FailOnEOF.Serial"
+  "Engine.BP.BPBufferSizeTest.SyncDeferredIdenticalUsage.*.Serial"
+  )
+list(JOIN EXCLUDE_EXPRESSIONS "|" TEST_EXCLUDE_STRING)
+set(CTEST_MEMCHECK_ARGS EXCLUDE "${TEST_EXCLUDE_STRING}")
+
 set(ADIOS_TEST_REPEAT 0)
 list(APPEND CTEST_UPDATE_NOTES_FILES "${CMAKE_CURRENT_LIST_FILE}")
 include(${CMAKE_CURRENT_LIST_DIR}/ci-common.cmake)
diff --git a/scripts/ci/cmake/ci-uo-sanitizer-ubsan.cmake b/scripts/ci/cmake/ci-uo-sanitizer-ubsan.cmake
index 91ed11016f..805ca8f843 100644
--- a/scripts/ci/cmake/ci-uo-sanitizer-ubsan.cmake
+++ b/scripts/ci/cmake/ci-uo-sanitizer-ubsan.cmake
@@ -15,6 +15,7 @@ ADIOS2_BUILD_EXAMPLES:BOOL=ON
 
 ADIOS2_USE_HDF5:STRING=ON
 ADIOS2_USE_MPI:STRING=OFF
+ADIOS2_USE_Python:STRING=OFF
 
 HDF5_C_COMPILER_EXECUTABLE:FILEPATH=/usr/bin/h5cc
 HDF5_DIFF_EXECUTABLE:FILEPATH=/usr/bin/h5diff
@@ -24,6 +25,13 @@ set(dashboard_track "Analysis")
 set(CTEST_CMAKE_GENERATOR "Ninja")
 set(CTEST_MEMORYCHECK_TYPE "UndefinedBehaviorSanitizer")
 
+list(APPEND EXCLUDE_EXPRESSIONS
+  "Install.*"
+  "Unit.FileTransport.FailOnEOF.Serial"
+  )
+list(JOIN EXCLUDE_EXPRESSIONS "|" TEST_EXCLUDE_STRING)
+set(CTEST_MEMCHECK_ARGS EXCLUDE "${TEST_EXCLUDE_STRING}")
+
 set(ADIOS_TEST_REPEAT 0)
 list(APPEND CTEST_UPDATE_NOTES_FILES "${CMAKE_CURRENT_LIST_FILE}")
 include(${CMAKE_CURRENT_LIST_DIR}/ci-common.cmake)
diff --git a/scripts/ci/images/opensuse-tw/build-images-opensuse-tw b/scripts/ci/images/opensuse-tw/build-images-opensuse-tw
new file mode 100755
index 0000000000..26c15b2d53
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/build-images-opensuse-tw
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# This script is used to build the openSUSE Tumbleweed images
+
+set -xeuo pipefail
+
+declare -r DOCKER_REPOSITORY=ghcr.io/ornladios/adios2
+declare -u ENABLE_PUSH=
+
+#=============================================================================
+# Functions
+
+# Function to build a docker image
+function docker_build() {
+  local docker_file=$1
+  local docker_tag=$2
+  local args=(--progress=plain --rm)
+
+  if [[ -n "${ENABLE_PUSH}" ]]; then
+    args+=("--push")
+  fi
+
+  sudo docker build "${args[@]}"  \
+    --file "${docker_file}" \
+    --tag "${DOCKER_REPOSITORY}:${docker_tag}-$(date +"%Y%m%d")" \
+    .
+}
+
+function usage() {
+  echo "Usage: $0 [-p <PROJECT>] [-m <MILESTONE>]"
+  echo "    -p    Push to the docker registry"
+  exit 0
+}
+
+#=============================================================================
+# Main
+while getopts "p" o; do
+  case "${o}" in
+    p) ENABLE_PUSH=true ;;
+    *) usage ;;
+  esac
+done
+
+docker_build opensuse-tw-sanitizer-base.dockerfile ci-opensuse-tw-sanitizer-base
+docker_build opensuse-tw-full-stack-onbuild.dockerfile ci-opensuse-tw-full-stack-onbuild
+docker_build opensuse-tw-asan.dockerfile ci-opensuse-tw-asan
+docker_build opensuse-tw-msan.dockerfile ci-opensuse-tw-msan
+docker_build opensuse-tw-tsan.dockerfile ci-opensuse-tw-tsan
+docker_build opensuse-tw-ubsan.dockerfile ci-opensuse-tw-ubsan
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-asan.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-asan.dockerfile
new file mode 100644
index 0000000000..8ceaf63b39
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-asan.dockerfile
@@ -0,0 +1,20 @@
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-sanitizer-base
+
+# Install core dev packages
+RUN zypper install -y --no-recommends libasan8 hdf5-devel && \
+    zypper clean --all
+
+# Install ZFP
+WORKDIR /opt/zfp
+RUN curl -L https://github.com/LLNL/zfp/releases/download/0.5.5/zfp-0.5.5.tar.gz | tar -xvz && \
+    cmake -GNinja -S zfp-0.5.5 -B build \
+      -DBUILD_SHARED_LIBS=ON \
+      -DCMAKE_BUILD_TYPE=Release \
+      -DCMAKE_INSTALL_PREFIX=/opt/zfp/0.5.5 \
+      && \
+    cmake --build build && \
+    cmake --install build && \
+    rm -rf zfp-0.5.5 build
+ENV PATH=/opt/zfp/0.5.5/bin:${PATH} \
+    LD_LIBRARY_PATH=/opt/zfp/0.5.5/lib64:${LD_LIBRARY_PATH} \
+    CMAKE_PREFIX_PATH=/opt/zfp/0.5.5:${CMAKE_PREFIX_PATH}
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-full-stack-onbuild.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-full-stack-onbuild.dockerfile
new file mode 100644
index 0000000000..1af5dee0ca
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-full-stack-onbuild.dockerfile
@@ -0,0 +1,125 @@
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-sanitizer-base
+
+# Set up some arguments
+ONBUILD ARG INSTALL_PREFIX
+ONBUILD ARG TOOLCHAIN_FILE
+ONBUILD ARG CFLAGS
+ONBUILD ARG CXXFLAGS
+ONBUILD ARG LLVM_USE_SANITIZER
+
+ENV CC=clang
+ENV CXX=clang++
+
+# Build and install libc++
+ONBUILD WORKDIR /root/llvm
+ONBUILD RUN git clone --branch llvmorg-17.0.6 --depth 1 \
+        https://github.com/llvm/llvm-project.git source
+ONBUILD RUN cmake -GNinja -B build -S source/runtimes \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
+            -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
+            -DLLVM_USE_SANITIZER=${LLVM_USE_SANITIZER} \
+             && \
+        cmake --build build --target install-cxxabi install-cxx
+
+# Copy in the toolchain
+ONBUILD COPY \
+    ${TOOLCHAIN_FILE} \
+    ${INSTALL_PREFIX}/toolchain.cmake
+
+# Build and install zlib
+ONBUILD WORKDIR /root/zlib
+ONBUILD RUN git clone --branch v1.2.11 --depth 1 \
+        https://github.com/madler/zlib.git source && \
+    cmake -GNinja -S source -B build \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=${INSTALL_PREFIX}/toolchain.cmake \
+        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
+        && \
+    cmake --build build && \
+    cmake --install build
+
+# Build and install bzip2
+ONBUILD WORKDIR /root/bzip2
+ONBUILD RUN git clone --branch bzip2-1.0.8 --depth 1 \
+        https://sourceware.org/git/bzip2.git source && \
+    cd source && \
+    sed -e "s_^CC=.*\$_CC=/usr/bin/clang_" \
+        -e "s_^CFLAGS=.*\$_CFLAGS=-fpic -fPIC -Wall -Winline -O2 ${CFLAGS} \$(BIGFILES)_" \
+        -i Makefile-libbz2_so && \
+    make -f Makefile-libbz2_so && \
+    sed -e "s_^CC=.*\$_CC=/usr/bin/clang_" \
+        -e "s_^PREFIX=.*\$_PREFIX=${INSTALL_PREFIX}_" \
+        -e "s_^CFLAGS=.*\$_CFLAGS=-Wall -Winline -O2 ${CFLAGS} \$(BIGFILES)_" \
+        -i Makefile && \
+    make install && \
+    install libbz2.so.1.0.8 ${INSTALL_PREFIX}/lib && \
+    ln -s -T libbz2.so.1.0.8 ${INSTALL_PREFIX}/lib/libbz2.so.1.0 && \
+    ln -s -T libbz2.so.1.0 ${INSTALL_PREFIX}/lib/libbz2.so
+
+# Build and install zeromq
+ONBUILD WORKDIR /root/zeromq
+ONBUILD RUN git clone --branch v4.3.2 --depth 1 \
+        https://github.com/zeromq/libzmq.git source && \
+    cmake -GNinja -S source -B build \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=${INSTALL_PREFIX}/toolchain.cmake \
+        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
+        && \
+    cmake --build build && \
+    cmake --install build
+
+
+# Build and install libpng
+ONBUILD WORKDIR /root/libpng
+ONBUILD RUN git clone --branch v1.6.9 --depth 1 \
+        https://git.code.sf.net/p/libpng/code.git source && \
+    cmake -GNinja -S source -B build \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=${INSTALL_PREFIX}/toolchain.cmake \
+        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
+        && \
+    cmake --build build && \
+    cmake --install build
+
+# Build and install hdf5
+ONBUILD WORKDIR /root/hdf5
+ONBUILD RUN git clone --branch hdf5-1_14_3 --depth=1 \
+        https://github.com/HDFGroup/hdf5.git source && \
+    cmake -GNinja -S source -B build \
+        -DBUILD_TESTING=OFF \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=${INSTALL_PREFIX}/toolchain.cmake \
+        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
+        && \
+    cmake --build build && \
+    cmake --install build
+
+# Build and install libfabric
+ONBUILD WORKDIR /root/libfabric
+ONBUILD RUN mkdir -p source build && \
+    curl -L \
+        https://github.com/ofiwg/libfabric/releases/download/v1.18.1/libfabric-1.18.1.tar.bz2 | \
+    tar -C source -xj --strip-components=1 && \
+    cd build && \
+    ../source/configure \
+        --prefix=${INSTALL_PREFIX} \
+        --disable-efa \
+        --disable-shm \
+        --disable-verbs \
+        CC="/usr/bin/clang ${CFLAGS} -L${INSTALL_PREFIX}/lib -Wl,-rpath,${INSTALL_PREFIX}/lib -Wno-unused-command-line-argument" \
+        CXX="/usr/bin/clang++ ${CXXFLAGS} -L${INSTALL_PREFIX}/lib -Wl,-rpath,${INSTALL_PREFIX}/lib -Wno-unused-command-line-argument -nostdinc++ -isystem ${INSTALL_PREFIX}/include/c++/v1 -stdlib=libc++" && \
+    make -j4 install
+
+# Build and install libffi
+ONBUILD WORKDIR /root/libffi
+ONBUILD RUN mkdir -p source build && \
+    curl -L \
+        https://github.com/libffi/libffi/releases/download/v3.3/libffi-3.3.tar.gz | \
+    tar -C source -xz --strip-components=1 && \
+    cd build && \
+    ../source/configure \
+        --prefix=${INSTALL_PREFIX} \
+        CC="/usr/bin/clang ${CFLAGS} -L${INSTALL_PREFIX}/lib -Wl,-rpath,${INSTALL_PREFIX}/lib -Wno-unused-command-line-argument" \
+        CXX="/usr/bin/clang++ ${CXXFLAGS} -L${INSTALL_PREFIX}/lib -Wl,-rpath,${INSTALL_PREFIX}/lib -Wno-unused-command-line-argument -nostdinc++ -isystem ${INSTALL_PREFIX}/include/c++/v1 -stdlib=libc++" && \
+    make -j4 install 
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-msan.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-msan.dockerfile
new file mode 100644
index 0000000000..e922d298f4
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-msan.dockerfile
@@ -0,0 +1,9 @@
+ARG INSTALL_PREFIX=/opt/msan
+ARG TOOLCHAIN_FILE=toolchain-msan.cmake
+ARG CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins"
+ARG CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins"
+ARG LLVM_USE_SANITIZER=MemoryWithOrigins
+
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-full-stack-onbuild AS tmp-stage
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-sanitizer-base
+COPY --from=tmp-stage /opt/msan/ /opt/msan/
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-sanitizer-base.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-sanitizer-base.dockerfile
new file mode 100644
index 0000000000..081d804c36
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-sanitizer-base.dockerfile
@@ -0,0 +1,33 @@
+FROM opensuse/tumbleweed
+LABEL maintainer "Vicente Adolfo Bolea Sanchez<vicente.bolea@kitware.com>"
+
+# Base dependencies for building ADIOS2 projects
+RUN zypper refresh && \
+    zypper update -y && \
+    zypper install -y --no-recommends \
+      awk \
+      bzip2 \
+      gzip \
+      bzip3-devel \
+      clang17 \
+      cmake \
+      curl \
+      file \
+      gcc \
+      gcc-c++ \
+      git \
+      git-lfs \
+      hdf5-devel \
+      libffi-devel \
+      libpng16-devel \
+      libunwind-devel \
+      llvm17 \
+      ninja \
+      patch \
+      python3-devel \
+      python3-numpy \
+      tar \
+      zeromq-devel \
+      zlib-devel \
+      && \
+    zypper clean --all
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-tsan.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-tsan.dockerfile
new file mode 100644
index 0000000000..295d1ac155
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-tsan.dockerfile
@@ -0,0 +1,9 @@
+ARG INSTALL_PREFIX=/opt/tsan
+ARG TOOLCHAIN_FILE=toolchain-tsan.cmake
+ARG CFLAGS="-fsanitize=thread"
+ARG CXXFLAGS="-fsanitize=thread"
+ARG LLVM_USE_SANITIZER=Thread
+
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-full-stack-onbuild AS tmp-stage
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-sanitizer-base
+COPY --from=tmp-stage /opt/tsan/ /opt/tsan/
diff --git a/scripts/ci/images/opensuse-tw/opensuse-tw-ubsan.dockerfile b/scripts/ci/images/opensuse-tw/opensuse-tw-ubsan.dockerfile
new file mode 100644
index 0000000000..35be3e7f31
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/opensuse-tw-ubsan.dockerfile
@@ -0,0 +1,5 @@
+FROM ghcr.io/ornladios/adios2:ci-opensuse-tw-sanitizer-base
+
+# Install core dev packages
+RUN zypper install -y --no-recommends libubsan1 libasan8 hdf5-devel zfp-devel && \
+    zypper clean --all
diff --git a/scripts/ci/images/opensuse-tw/toolchain-msan.cmake b/scripts/ci/images/opensuse-tw/toolchain-msan.cmake
new file mode 100644
index 0000000000..724307c620
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/toolchain-msan.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_C_COMPILER /usr/bin/clang)
+set(CMAKE_CXX_COMPILER /usr/bin/clang++)
+set(_link_flags "-stdlib=libc++ -L /opt/msan/lib -lc++abi -Wl,-rpath,/opt/msan/lib -Wno-unused-command-line-argument")
+set(_compile_flags "-nostdinc++ -isystem /opt/msan/include -isystem /opt/msan/include/c++/v1 -fsanitize=memory -fsanitize-memory-track-origins")
+set(CMAKE_EXE_LINKER_FLAGS_INIT ${_link_flags})
+set(CMAKE_SHARED_LINKER_FLAGS_INIT ${_link_flags})
+set(CMAKE_C_FLAGS_INIT ${_compile_flags})
+set(CMAKE_CXX_FLAGS_INIT ${_compile_flags})
+set(CMAKE_PREFIX_PATH /opt/msan)
diff --git a/scripts/ci/images/opensuse-tw/toolchain-tsan.cmake b/scripts/ci/images/opensuse-tw/toolchain-tsan.cmake
new file mode 100644
index 0000000000..2f69fdb0d8
--- /dev/null
+++ b/scripts/ci/images/opensuse-tw/toolchain-tsan.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_C_COMPILER /usr/bin/clang)
+set(CMAKE_CXX_COMPILER /usr/bin/clang++)
+set(_link_flags "-stdlib=libc++ -L /opt/tsan/lib -Wl,-rpath,/opt/tsan/lib -Wno-unused-command-line-argument")
+set(_compile_flags "-nostdinc++ -isystem /opt/tsan/include/c++/v1 -fsanitize=thread")
+set(CMAKE_EXE_LINKER_FLAGS_INIT ${_link_flags})
+set(CMAKE_SHARED_LINKER_FLAGS_INIT ${_link_flags})
+set(CMAKE_C_FLAGS_INIT ${_compile_flags})
+set(CMAKE_CXX_FLAGS_INIT ${_compile_flags})
+set(CMAKE_PREFIX_PATH /opt/tsan)