1# The MIT License (MIT)
2#
3# Copyright (c)
4#   2013 Matthew Arsenault
5#   2015-2016 RWTH Aachen University, Federal Republic of Germany
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in all
15# copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24
25# Helper function to get the language of a source file.
26function (sanitizer_lang_of_source FILE RETURN_VAR)
27    get_filename_component(LONGEST_EXT "${FILE}" EXT)
28    # If extension is empty return. This can happen for extensionless headers
29    if("${LONGEST_EXT}" STREQUAL "")
30       set(${RETURN_VAR} "" PARENT_SCOPE)
31       return()
32    endif()
33    # Get shortest extension as some files can have dot in their names
34    string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT})
35    string(TOLOWER "${FILE_EXT}" FILE_EXT)
36    string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
37
38    get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
39    foreach (LANG ${ENABLED_LANGUAGES})
40        list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
41        if (NOT ${TEMP} EQUAL -1)
42            set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
43            return()
44        endif ()
45    endforeach()
46
47    set(${RETURN_VAR} "" PARENT_SCOPE)
48endfunction ()
49
50
51# Helper function to get compilers used by a target.
52function (sanitizer_target_compilers TARGET RETURN_VAR)
53    # Check if all sources for target use the same compiler. If a target uses
54    # e.g. C and Fortran mixed and uses different compilers (e.g. clang and
55    # gfortran) this can trigger huge problems, because different compilers may
56    # use different implementations for sanitizers.
57    set(BUFFER "")
58    get_target_property(TSOURCES ${TARGET} SOURCES)
59    foreach (FILE ${TSOURCES})
60        # If expression was found, FILE is a generator-expression for an object
61        # library. Object libraries will be ignored.
62        string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
63        if ("${_file}" STREQUAL "")
64            sanitizer_lang_of_source(${FILE} LANG)
65            if (LANG)
66                list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID})
67            endif ()
68        endif ()
69    endforeach ()
70
71    list(REMOVE_DUPLICATES BUFFER)
72    set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE)
73endfunction ()
74
75
76# Helper function to check compiler flags for language compiler.
77function (sanitizer_check_compiler_flag FLAG LANG VARIABLE)
78    if (${LANG} STREQUAL "C")
79        include(CheckCCompilerFlag)
80        check_c_compiler_flag("${FLAG}" ${VARIABLE})
81
82    elseif (${LANG} STREQUAL "CXX")
83        include(CheckCXXCompilerFlag)
84        check_cxx_compiler_flag("${FLAG}" ${VARIABLE})
85
86    elseif (${LANG} STREQUAL "Fortran")
87        # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible
88        # with older Cmake versions, we will check if this module is present
89        # before we use it. Otherwise we will define Fortran coverage support as
90        # not available.
91        include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED)
92        if (INCLUDED)
93            check_fortran_compiler_flag("${FLAG}" ${VARIABLE})
94        elseif (NOT CMAKE_REQUIRED_QUIET)
95            message(STATUS "Performing Test ${VARIABLE}")
96            message(STATUS "Performing Test ${VARIABLE}"
97                " - Failed (Check not supported)")
98        endif ()
99    endif()
100endfunction ()
101
102
103# Helper function to test compiler flags.
104function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX)
105    set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY})
106
107    get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
108    foreach (LANG ${ENABLED_LANGUAGES})
109        # Sanitizer flags are not dependend on language, but the used compiler.
110        # So instead of searching flags foreach language, search flags foreach
111        # compiler used.
112        set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
113        if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS)
114            foreach (FLAG ${FLAG_CANDIDATES})
115                if(NOT CMAKE_REQUIRED_QUIET)
116                    message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]")
117                endif()
118
119                set(CMAKE_REQUIRED_FLAGS "${FLAG}")
120                unset(${PREFIX}_FLAG_DETECTED CACHE)
121                sanitizer_check_compiler_flag("${FLAG}" ${LANG}
122                    ${PREFIX}_FLAG_DETECTED)
123
124                if (${PREFIX}_FLAG_DETECTED)
125                    # If compiler is a GNU compiler, search for static flag, if
126                    # SANITIZE_LINK_STATIC is enabled.
127                    if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU"))
128                        string(TOLOWER ${PREFIX} PREFIX_lower)
129                        sanitizer_check_compiler_flag(
130                            "-static-lib${PREFIX_lower}" ${LANG}
131                            ${PREFIX}_STATIC_FLAG_DETECTED)
132
133                        if (${PREFIX}_STATIC_FLAG_DETECTED)
134                            set(FLAG "-static-lib${PREFIX_lower} ${FLAG}")
135                        endif ()
136                    endif ()
137
138                    set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING
139                        "${NAME} flags for ${COMPILER} compiler.")
140                    mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
141                    break()
142                endif ()
143            endforeach ()
144
145            if (NOT ${PREFIX}_FLAG_DETECTED)
146                set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING
147                    "${NAME} flags for ${COMPILER} compiler.")
148                mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
149
150                message(WARNING "${NAME} is not available for ${COMPILER} "
151                        "compiler. Targets using this compiler will be "
152                        "compiled without ${NAME}.")
153            endif ()
154        endif ()
155    endforeach ()
156endfunction ()
157
158
159# Helper to assign sanitizer flags for TARGET.
160function (sanitizer_add_flags TARGET NAME PREFIX)
161    # Get list of compilers used by target and check, if sanitizer is available
162    # for this target. Other compiler checks like check for conflicting
163    # compilers will be done in add_sanitizers function.
164    sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
165    list(LENGTH TARGET_COMPILER NUM_COMPILERS)
166    if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")
167        return()
168    endif()
169
170    # Set compile- and link-flags for target.
171    set_property(TARGET ${TARGET} APPEND_STRING
172        PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
173    set_property(TARGET ${TARGET} APPEND_STRING
174        PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}")
175    set_property(TARGET ${TARGET} APPEND_STRING
176        PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
177endfunction ()
178