#
# This file is part of the GROMACS molecular simulation package.
#
# Copyright 2015- The GROMACS Authors
# and the project initiators Erik Lindahl, Berk Hess and David van der Spoel.
# Consult the AUTHORS/COPYING files and https://www.gromacs.org for details.
#
# GROMACS is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# GROMACS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with GROMACS; if not, see
# https://www.gnu.org/licenses, or write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
#
# If you want to redistribute modifications to GROMACS, please
# consider that scientific software is very special. Version
# control is crucial - bugs must be traceable. We will be happy to
# consider code for inclusion in the official distribution, but
# derived work must not be called official GROMACS. Details are found
# in the README & COPYING files - if they are missing, get the
# official version at https://www.gromacs.org.
#
# To help us fund GROMACS development, we humbly ask that you cite
# the research papers on the package. Check out https://www.gromacs.org.

# This file implements the custom build type "MSAN", to be used to run
# the Memory Sanitizer checker in clang. The code here follows the
# approaches documented at
# https://code.google.com/p/memory-sanitizer/wiki/LibcxxHowTo and
# https://code.google.com/p/memory-sanitizer/wiki/MemorySanitizer. In
# particular, the dependency stack must also be built with
# MemorySanitizer - notably including the C++ standard library, and
# notably excluding the C and math libraries.
#
# CMake cache variable GMX_MSAN_PATH should be set (e.g. on the
# command line) to point to the location where the dependencies that
# have been built with MemorySanitizer support can be found. This will
# be preprended to preprocessor and linker search paths. The MSAN
# build type is highly unlikely to work usefully without this variable
# set appropriately, and the dependencies installed there. In
# particular, suitable zlib needs to be installed there.
#
# Where GROMACS can optionally use bundled dependency code, such as
# fftpack and XDR, only the bundled code is supported. MSAN build type
# does not support static linking at this time.
#
# This build type needs to set linker flags in order to function, and
# CMake does not directly support linker flags that are specific to a
# build type (except on Windows). Thus, it is not clear whether
# changing the build type to/from MSAN, or any other modification to
# CMAKE_*_LINKER_FLAGS will work well. If in doubt, clean the build
# tree and start again.
function(gmxManageMsanBuild)
    string(TOUPPER "${CMAKE_BUILD_TYPE}" _cmake_build_type)

    if (NOT "${_cmake_build_type}" STREQUAL "MSAN")
        # Nothing needs to be done now
        return()
    endif()

    set(_flags "-O1 -g -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer")

    foreach(_language C CXX)
        string(REPLACE "X" "+" _human_readable_language ${_language})
        if (CMAKE_${_language}_COMPILER_ID MATCHES "Clang")
            if(${_language} MATCHES CXX)
                set(_language_flags "${_flags} -nostdinc++")
                if(GMX_MSAN_PATH)
                    set(_language_flags "${_language_flags} -isystem ${GMX_MSAN_PATH}/include -isystem ${GMX_MSAN_PATH}/include/c++/v1")
                endif()
            else()
                set(_language_flags "${_flags}")
            endif()
            set(CMAKE_${_language}_FLAGS_MSAN ${_language_flags} CACHE STRING "${_human_readable_language} flags for Memory Sanitizer" FORCE)
            mark_as_advanced(CMAKE_${_language}_FLAGS_MSAN)
        else()
            message(FATAL_ERROR "The Memory Sanitizer build is only available with clang ${_human_readable_language} compiler, but it was ${CMAKE_${_language}_COMPILER_ID}.")
        endif()
    endforeach()

    # Per-build-type linker flags like CMAKE_EXE_LINKER_FLAGS_MSAN
    # only seem to be supported by CMake on Windows, so we can't use
    # that.
    set(_linker_flags "-fsanitize=memory -fsanitize-memory-track-origins")
    if(GMX_MSAN_PATH)
        set(_linker_flags "${_linker_flags} -lc++abi -lc++ -Wl,-rpath,${GMX_MSAN_PATH}/lib -L${GMX_MSAN_PATH}/lib")
    endif()
    set(CMAKE_EXE_LINKER_FLAGS "${_linker_flags} ${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE)
    set(CMAKE_SHARED_LINKER_FLAGS "${_linker_flags} ${CMAKE_SHARED_LINKER_FLAGS}" PARENT_SCOPE)
#   Static linking is not supported for this build type
#    set(CMAKE_STATIC_LINKER_FLAGS ${_linker_flags} CACHE STRING "Linker flags for Memory Sanitizer" FORCE)

    set(GMX_FFT_LIBRARY "fftpack" CACHE STRING "Only fftpack is supported with MSAN")
    set(GMX_SYSTEM_XDR OFF CACHE STRING "Only internal XDR is supported with MSAN")
endfunction()

gmxManageMsanBuild()
